/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2011  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.measuring.calldep;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.graph.GraphEdge;
import org.sosy_lab.ccvisu.graph.GraphVertex;
import org.sosy_lab.ccvisu.graph.NameVisibility;
import org.sosy_lab.ccvisu.measuring.calldep.colors.ColorProvider;
import org.sosy_lab.ccvisu.measuring.calldep.colors.VertexColoring;
import org.sosy_lab.common.LogManager;
import org.sosy_lab.util.ProgressingBase;
import org.sosy_lab.util.interfaces.Progressing;

public class DependencyCalculator extends ProgressingBase implements Runnable, Progressing {

  private GraphData         graph;
  private DependencyOptions depOptions;
  private final LogManager  logger;

  public DependencyCalculator(LogManager logger, GraphData graph,
      DependencyOptions depOptions) {

    this.logger = logger;
    this.graph = graph;
    this.depOptions = depOptions;
  }

  public void calculateAndSetDegree() {
    if (graph.getEdges().isEmpty()) {
      logger.log(Level.WARNING, "Cannot perform measurement analysis on empty graph.");
      return;
    }

    List<VertexContainer> vertexList = getListOfVertexContainers();

    // normalize
    Collections.sort(vertexList);
    double max = vertexList.get(vertexList.size() - 1).getVertexMeasure();
    double min = vertexList.get(0).getVertexMeasure();
    double sum = 0;

    VertexColoring coloring = ColorProvider.getInstance().getColoringObject(
        depOptions.getColoring(), min, max);

    for (VertexContainer vertexContainer : vertexList) {
      GraphVertex vertex = vertexContainer.getVertex();

      vertex.setColor(coloring.getColor(vertexContainer.getVertexMeasure()));
      vertex.setTooltip(vertexContainer.getClass().getSimpleName() + ": "
          + String.valueOf(vertexContainer.getVertexMeasure()));
      sum += vertexContainer.getVertexMeasure();

      // disable showing of labels from previous runs of the algorithm
      vertex.unsetShowName(NameVisibility.Priority.DEPDEGREE);
    }

    // show labels
    int numberOfShownLabels = depOptions.getShowLabels();
    Collections.reverse(vertexList);
    Iterator<VertexContainer> iterator = vertexList.iterator();
    for (int i = 0; i < numberOfShownLabels && iterator.hasNext(); i++) {
      iterator.next().getVertex().setShowName(NameVisibility.Priority.DEPDEGREE, true);
    }

    logger.log(Level.INFO, "Overall dependency degree: " + sum);
    logger.log(Level.INFO, "Min dependency: " + min);
    logger.log(Level.INFO, "Max dependency: " + max);

    graph.notifyAboutLayoutChange(new EventObject(this));
  }

  public List<VertexContainer> getListOfVertexContainers() {
    Map<GraphVertex, VertexContainer> vertices =
        new HashMap<GraphVertex, VertexContainer>(graph.getVertices().size());

    // create the container constructor
    Constructor<? extends VertexContainer> algorithm = null; // gets init. in try/catch
    try {
      algorithm = depOptions.getAlgorithm().getConstructor(GraphVertex.class);
    } catch (Exception e) {
      logger.logUserException(Level.SEVERE, e, "The dependency algorithm "
          + "contructor couldn't be created");
      return new ArrayList<VertexContainer>(); // there are no containers yet
    }

    // put each vertex together with its container in the map
    for (GraphVertex vertex : graph.getVertices()) {
        VertexContainer container = null; // gets init. in try/catch
        try {
          container = algorithm.newInstance(vertex);
        } catch (Exception e) {
          logger.logUserException(Level.SEVERE, e, "The dependency algorithm "
              + "couldn't be created");
          new ArrayList<VertexContainer>(vertices.values());
        }
        vertices.put(vertex, container);
    }

    // add outgoing edges to the corresponding container
    for (GraphEdge edge : graph.getEdges()) {
      vertices.get(edge.getSource()).addEdge(edge);
    }

    // return a list of the created containers
    return new ArrayList<VertexContainer>(vertices.values());
  }

  @Override
  public void run() {
    calculateAndSetDegree();
  }
}
